$(document).ready(function() {
	var i;

	SkOOlix.gipedo = [];
	for (i = 0; i < SkOOlix.multi; i++) {
		SkOOlix.gipedo.push(new Gipedo().createDOM().stisimoCpanel());
	}
	$(window).trigger('resize');
});

$(window).on('resize', function() {
	var dh, dw, rows, cols;

	SkOOlix.gipedoWalk(function() {
		this.terenDOM.empty();
	});

	dh = $(document).innerHeight();
	dw = $(document).innerWidth() - (SkOOlix.multi * SkOOlix.size * 0.2);

	rows = parseInt(dh / SkOOlix.size) - 1;
	cols = parseInt(dw / (SkOOlix.size * SkOOlix.multi)) - 1;

	SkOOlix.gipedoWalk(function() {
		this.stisimoTeren(rows, cols);
	});
});

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Μια βασική κλάση αντικειμένων της εφαρμογής είναι η κλάση "Gipedo" η οποία παριστά
// έναν ολοκληρωμένο χώρο κίνησης σκουληκιού. Ο χώρος αυτός περιλαμβάνει ένα πλακόστρωτο
// τερέν, ένα σκουλήκι και ένα control panel με το οποίο μπορούμε να ρυθμίσουμε κάποιες
// παραμέτρους της κίνησης και του τερέν. Οι πράμετροι του γηπέδου τίθενται αρχικά στις
// default τιμές που ίσως έχουν τροποποιηθεί στο URL.

Gipedo = function() {
	this.speed = SkOOlix.speed;
	this.size = SkOOlix.size;
	this.epimoni = SkOOlix.epimoni;
	this.patoma = 0;
	this.ixnos = false;
}

// Η μέθοδος "patomaEpomeno" επιστρέφει το αρχείο εικόνας που θα χρησιμοποιηθεί σε
// περίπτωση αλλαγής των πλακιδίων πλακόστρωσης του τερέν του γηπέδου. Τα διαθέσιμα
// είδη πλακιδίων επιλέγονται με κυκλικό τρόπο από την παλέτα πλακιδίων που πέρασε
// στο παρόν από την PHP και περιλαμβάνει όλα τα αρχεία εικόνας που βρέθηκαν στο
// directory "ikona/plakidio". Καλό είναι οι εικόνες αυτές να είναι τετράγωνες,
// αλλά και αν δεν είναι το πρόγραμμα τις τετραγωνίζει.

Gipedo.prototype.patomaEpomeno = function() {
	return (this.patoma + 1) % SkOOlix.paleta.length;
}

// Η μέθοδος "createDOM" δημιουργεί το DOM element του γηπέδου και το εντάσσει στο
// DOM απλώς προσθέτοντάς το στο body. Πρόκειται ουσιαστικά για πίνακα 1Χ2 όπου στην
// πρώτη στήλη (αριστερά) εμφανίζεται το τερέν και στη δεύτερη στήλη (δεξιά) εμφανίζεται
// το control panel.

Gipedo.prototype.createDOM = function() {
	if (this.hasOwnProperty('DOM')) this.DOM.remove();
	this.DOM = $('<table>').addClass('gipedo').appendTo($(document.body)).
	append($('<tbody>').append($('<tr>').

	// Κρατάμε το DOM element του τερέν ως property του γηπέδου για να έχουμε
	// γρήγορη και εύκολη πρόσβαση.

	append(this.terenDOM = $('<td>').addClass('teren')).

	// Το ίδιο και με το control panel.

	append(this.cpanelDOM = $('<td>').addClass('cpanel'))));
	return this;
}

// Η μέθοδος "stisimoCpanel" δημιουργεί το control panel ως μια στήλη εργαλείων και
// ενδείξεων που αφορούν κυρίως στις παραμέτρους του γηπέδου, δηλαδή στις παραμέτους
// κίνησης του σκουληκιού, στην εμφάνιση του τερέν κλπ.

Gipedo.prototype.stisimoCpanel = function() {
	var gipedo = this, size = parseInt(SkOOlix.size * 0.8) + 'px';

	this.cpanelDOM.css('display', 'none').

	/////////////////////////////////////////////////////////////////////////////////////////

	// Αύξηση της ταχύτητας. Ουσιαστικά μειώνουμε στο μισό το χρόνο που μεσολαβεί
	// μεταξύ δύο διαδοχικών κινήσεων του σκουληκιού. Τηρείται κάποιο ελάχιστο όριο
	// το οποίο ίσως να μην αντανακλά σε πραγματικό χρόνο.

	append($('<img>').attr({
		title: 'Fast',
		src: 'ikona/cpanel/grigora.png',
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.speed = parseInt(gipedo.speed / 2);
		if (gipedo.speed < SkOOlix.speedMin) gipedo.speed = SkOOlix.speedMin;
		gipedo.cpanelSpeedEndixi(true);
	})).

	// Ένδειξη χρόνου που μεσολαβεί μεταξύ δύο διαδοχικών κινήσεων του σκουληκιού.
	// Ο χρόνος αυτός είναι σε ms.
	
	append(this.cpanelSpeedDOM = $('<div>').attr('title', 'Speed')).

	// Μείωση της ταχύτητας. Ουσιαστικά διπλασιάζουμε το χρόνο που μεσολαβεί που
	// μεσολαβεί μεταξύ δύο διαδοχικών κινήσεων του σκουληκιού.

	append($('<img>').attr({
		title: 'Slow',
		src: 'ikona/cpanel/arga.png',
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.speed *= 2;
		if (gipedo.speed > SkOOlix.speedMax) gipedo.speed = SkOOlix.speedMax;
		gipedo.cpanelSpeedEndixi(true);
	})).

	/////////////////////////////////////////////////////////////////////////////////////////

	// Αύξηση του βαθμού ελευθερίας του σκουληκιού. Βαθμός ελευθερίας είναι ένας θετικός
	// ακέραιος που δείχνει περίπου κάθε πότε το σκουλήκι θα μπει στη διαδικασία πιθανής
	// αλλαγής πορείας. Αν, π.χ. ο βαθμός ελευθερίας είναι 5, τότε μια φορά στις πέντε το
	// σκουλήκι θα μπει στη διαδικασία μιας πιθανής αλλαγής πορείας. Αν ο βαθμός ελευθερίας
	// είναι 1, τότε η αποφάσεις αλλαγής πορείας είναι συνεχείς.

	append($('<img>').attr({
		title: 'Cuckoo',
		src: 'ikona/skolix/zoiro.png',
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.epimoni--;
		if (gipedo.epimoni < 1) gipedo.epimoni = 1;
		gipedo.cpanelEpimoniEndixi();
	})).

	// Ένδειξη του βαθμού ελευθερίας του σκουληκιού.

	append(this.cpanelEpimoniDOM = $('<div>').attr('title', 'Stability')).

	// Μείωση του βαθμού ελευθερίας του σκουληκιού. Η κίνηση του σκουληκιού γίνεται σταθερότερη.

	append($('<img>').attr({
		title: 'Steady',
		src: 'ikona/skolix/xalaro.png',
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.epimoni++;
		if (gipedo.epimoni > 99) gipedo.epimoni = 99;
		gipedo.cpanelEpimoniEndixi();
	})).

	/////////////////////////////////////////////////////////////////////////////////////////

	// Αλλαγή πλακιδίων τερέν. Υπάρχουν μια γκάμα πλακιδίων από την οποία επιλέγεται
	// η νέα διακόσμηση. Τα διάφορα είδη πλακιδίων εφαρμόζονται διαδοχικά.

	append($('<img>').attr({
		title: 'Tile',
		src: 'ikona/plakidio/' + SkOOlix.paleta[this.patomaEpomeno()],
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.patoma = gipedo.patomaEpomeno();
		$(this).attr('src', 'ikona/plakidio/' + SkOOlix.paleta[gipedo.patomaEpomeno()]);
		gipedo.plakidioWalk(function() {
			this.patomaDOM.attr('src', 'ikona/plakidio/' + SkOOlix.paleta[gipedo.patoma]);
		});
	})).

	// Εμφάνιση/απόκρυψη πλήθους διαβάσεων σε κάθε πλακίδιο του τερέν.

	append($('<img>').attr({
		title: 'Trace',
		src: 'ikona/cpanel/ixnos.png',
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.ixnos = !gipedo.ixnos;
		gipedo.DOM.find('.ixnos').css('display', gipedo.ixnos ? 'inline' : 'none');
	})).

	/////////////////////////////////////////////////////////////////////////////////////////

	// Το πλήκτρο εκκίνησης χρησιμοποιείται για να εκκινήσουμε το σκουλήκι. Όταν το
	// σκουλήκι έχει μόλις δημιουργηθεί, μπορούμε να το εκκινήσουμε και με κλικ στην
	// κεφαλή του σκουληκιού.

	append(this.cpanelSTART = $('<img>').attr({
		title: 'Start',
		src: 'ikona/cpanel/start.png',
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.skolix.kinisiStart();
	})).

	/////////////////////////////////////////////////////////////////////////////////////////

	// Το πλήκτρο διακοπής διακόπτει την κίνηση του σκουληκιού. Η κίνηση μπορεί να
	// συνεχιστεί με κλικ στο πλήκτρο εκκίνησης, ή εμμέσως με τα πλήκτρα αυξομείωσης
	// της ταχύτητας.

	append(this.cpanelSTOP = $('<img>').attr({
		title: 'Stop',
		src: 'ikona/cpanel/stop.png',
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.skolix.kinisiStop();
	})).

	/////////////////////////////////////////////////////////////////////////////////////////

	// Το πλήκτρο απόκρυψης υποψήφιας κεφαλής σκουληκιού κάνει ακριβώς αυτό. Πράγματι,
	// όταν κάνουμε κλικ σε οποιοδήποτε πλακίδιο του τερέν, θέτουμε το συγκεκριμένο
	// πλακίδιο ως υποψήφιο πλακίδιο της κεφαλής νέου σκουληκιού. Αν επαναλάβουμε το
	// κλικ στο ήδη υποψήφιο πλακίδιο, τότε δημιουργείται νέο σκουλήκι με την κεφαλή
	// στο συγκεκριμένο πλακίδιο.

	append(this.cpanelKCC = $('<img>').attr({
		title: 'Cancel',
		src: 'ikona/skolix/kefali.png',
	}).on('click', function(e) {
		e.stopPropagation();
		gipedo.kefaliCandiClear();
	}));

	/////////////////////////////////////////////////////////////////////////////////////////

	this.cpanelDOM.children('img').addClass('cpanelIcon');
	this.cpanelDOM.children('div').addClass('cpanelText');
	this.cpanelDOM.children().css({
		display: 'block',
		width: size,
		height: size,
		marginTop: '8px',
	});
	this.cpanelKCC.css('display', 'none');
	this.cpanelSTART.css('display', 'none');
	this.cpanelSTOP.css('display', 'none');

	this.
	cpanelSpeedEndixi().
	cpanelEpimoniEndixi().
	cpanelDOM.css('display', 'table-cell');

	return this;
}

// Η μέθοδος "cpanelSpeedEndixi" αναλαμβάνει να ενημερώσει την ένδειξη της ταχύτητας
// του σκουληκιού. Η ταχύτητα εμφανίζεται ως διάρκεια του πλάνου κινήσεως, δηλαδή για
// πόσο χρόνο θα μένει ακίνητο το σκουλήκι πριν κάνει την επόμενη κίνηση. Η διάρκεια
// κάθε πλάνου τίθεται σε ms, αλλά η παρούσα ένδειξη παρουσιάζει τον λογάριθμο αυτής
// της διάρκειας με βάση το 2, καθώς αυτοί οι χρόνοι διπλασιάζονται ή υποδιπλασιάζονται,
// οπότε έχουν πάντοτε ως τιμές κάποιες δυνάμεις του 2.
//
// Η μέθοδος δέχεται ως παράμετρο και έναν δείκτη (επαν)έναρξης κίνησης του σκουληκιού.
// Αν ο δείκτης αυτός είναι αληθής, τότε μετά την ενημέρωση της ένδειξης της ταχύηττας,
// το σκουλήκι τίθεται σε κίνηση.

Gipedo.prototype.cpanelSpeedEndixi = function(kinisi) {
	var dt = parseInt(Math.log(this.speed) / Math.log(2));
	this.cpanelSpeedDOM.html(dt > 10 ? '&infin;' : dt);

	if (kinisi && this.skolix) this.skolix.kinisiStart();
	return this;
}

// Η μέθοδος "cpanelEpimoniEndixi" ενημερώνει την ένδειξη του βαθμού σταθερότητας της
// κίνησης του σκουληκιού. Βαθμός σταθερότητας είναι το αντίθετο του βαθμού ελευθερίας
// και δείχνει περίπου κάθε πόσες κινήσεις το σκουλήκι θα αποφασίζει για μια νέα πορεία.
// Αν, π.χ. ο βαθμός σταθερότητας είναι 5, τότε το σκουλήκι θα παίρνει απόφαση νέας
// πορείας περίπου μια φορά κάθε πέντε κινήσεις. Επειδή όμως η πορεία που θα αποφασιστεί
// είναι μια από τέσσερις (πάνω, δεξιά, κάτω, αριστερά), τελικά το σκουλήκι θα αλλάζει
// πορεία περίπου τρεις φορές στις τέσσερις, άρα η τελική πιθανότητα αλλαγής πορείας
// είναι (0.75 / N), όπου N είναι ο βαθμός σταθερότητας.

Gipedo.prototype.cpanelEpimoniEndixi = function() {
	this.cpanelEpimoniDOM.text(this.epimoni);
	return this;
}

// Η μέθοδος "stisimoTeren" δέχεται ως παραμέτρους τις διαστάσεις του τερέν και δημιουργεί
// το τερέν και το σχετικό DOM element. Το τερέν αποτελείται από πλακίδια και διαρθρώνεται
// κατά γραμμή.

Gipedo.prototype.stisimoTeren = function(rows, cols) {
	var grami, row, col;

	this.rows = rows;
	this.cols = cols;

	this.plakidio = [];
	delete this.kefali;

	this.terenDOM.empty();
	for (row = 1; row <= rows; row++) {
		grami = $('<div>').addClass('grami').appendTo(this.terenDOM);
		for (col = 1; col <= cols; col++) {
			this.plakidio[row + 'X' + col] = new Plakidio(this, row, col, grami);
		}
	}

	return this;
}

// Η μέθοδος "plakidioWalk" διατρέχει τα πλακίδια του τερέν του γηπέδου και για
// κάθε πλακίδιο καλεί callback function ως μέθοδο του πλακιδίου. Η περιδάβαση
// των πλακιδίων γίνεται κατά γραμμή και στήλη.

Gipedo.prototype.plakidioWalk = function(callback) {
	var row, col;

	for (row = 1; row <= this.rows; row++) {
		for (col = 1; col <= this.cols; col++) {
			callback.apply(this.plakidioGet(row, col));
		}
	}

	return this;
}

// Η μέθοδος "plakidioGet" δέχεται ως παραμέτρους μια γραμμή και μια στήλη του τερέν
// και επιστρέφει το αντίστοιχο πλακίδιο.

Gipedo.prototype.plakidioGet = function(row, col) {
	return this.plakidio[row + 'X' + col];
}

// Η μέθοδος "skolixCreate" δημιουργεί το σκουλήκι τού ανά χείρας γηπέδου. Καλού κακού
// διαγράφει πρώτα τυχόν προηγούμενο σκουλήκι του συγκεκριμένου γηπέδου. Η δημιουργία
// του σκουληκιού περιλαμβάνει και τη δημιουργία των DOM elements των κυττάρων του
// σκουληκιού.

Gipedo.prototype.skolixCreate = function() {
	this.skolixRemove().
	skolix = new Skolix(this).createDOM();
	this.cpanelSTART.css('display', 'block');
	return this;
}

// Η μέθοδος "skolixRemove" διαγράφει το σκουλήκι του ανά χείρας γηπέδου, εφόσον αυτό
// υπάρχει. Η διαγραφή περιλαμβάνει διαγραφή των σχετικών DOM elements και απελευθέρωση
// των πλακιδίων στα οποία εκτείνονταν το σκουλήκι.

Gipedo.prototype.skolixRemove = function() {
	if (!this.hasOwnProperty('skolix')) return this;
	this.skolix.kitaroWalk(function() {
		this.plakidioGet().ekenosi();
	}).removeDOM();
	delete this.skolix;
	return this;
}

Gipedo.prototype.kefaliCandiClear = function() {
	var skolix = this.skolix;

	$(this.terenDOM).find('.plakidioKefalis').remove();
	delete this.kefali;
	this.cpanelKCC.css('display', 'none');
	if (skolix) this.skolix.kefaliIcon('kinisi');
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Η κλάση "Plakidio" παριστά τα πλακίδια του τερέν. Για τη δημιουργία αντικειμένων
// της κλάσης αυτής απαιτείται το γήπεδο, ο αριθμός γραμμής, ο αριθμός στήλης και
// το DOM element της γραμμής του πλακιδίου.

Plakidio = function(gipedo, row, col, gramiDOM) {
	this.gipedo = gipedo;
	this.row = row;
	this.col = col;
	this.ixnos = 0;
	this.createDOM(gramiDOM);
	return this;
}

// Με τη μέθοδο "createDOM" το πλακίδιο αποκτά DOM element και εντάσσεται στο DOM.
// Το τερέν είναι διαρθρωμένο σε γραμμές και ως παράμετρο περνάμε το DOM element
// της γραμμής στην οποία προστίθεται το πλακίδιο.

Plakidio.prototype.createDOM = function(gramiDOM) {
	var plakidio = this, gipedo = this.gipedo;

	if (this.hasOwnProperty('DOM')) this.DOM.remove();
	this.DOM = $('<div>').addClass('plakidio').appendTo(gramiDOM).
	append(this.patomaDOM = $('<img>').css({
		width: SkOOlix.size + 'px',
		height: SkOOlix.size + 'px',
	}).
	attr('src', 'ikona/plakidio/' + SkOOlix.paleta[gipedo.patoma])).
	append(this.ixnosDOM = $('<div>').addClass('ixnos').
	css('display', gipedo.ixnos ? 'inline' : 'none')).
	on('click', function(e) {
		e.stopPropagation();
		plakidio.plakidioClick();
	});

	return this;
}

// Η μέθοδος "plakidioClick" καλείται κάθε φορά που κάνουμε κλικ σε κάποιο πλακίδιο.
// Η συμπεριφορά του προγράμματος είναι κάπως παράξενη σ' αυτό το σημείο, αλλά
// συνοψίζεται στα εξής: Το πρώτο κλικ σε κάποιο πλακίδιο θέτει το συγκεκριμένο
// πλακίδιο σε κατάσταση υποψηφιότητας ως κεφαλή του σκουληκιού. Δεύτερο κλικ
// στο ίδιο πλακίδιο καθιστά το πλακίδιο κεφαλή και δημιουργεί το σκουλήκι, το
// οποίο θα ξεκινήσει την τρελή του πορεία με κλικ στο πλακίδιο κεφαλής.

Plakidio.prototype.plakidioClick = function() {
	var gipedo = this.gipedo, kefali = this.gipedo.kefali, skolix = this.gipedo.skolix;

	// Πρώτα ελέγχουμε την περίπτωση κατά την οποία έγινε κλικ σε πλακίδιο ξένο
	// προς τυχόν υποψήφιο πλακίδιο κεφαλής.

	if (kefali != this) {
		// Αν δεν υπάρχει σκουλήκι, δημιουργούμε νέο με κεφαλή στο πλακίδιο
		// που κάναμε κλικ.

		if (!skolix) {
			gipedo.kefali = this;
			gipedo.skolixCreate();
			return this;
		}

		// Υπάρχει ήδη σκουλήκι, οπότε ακυρώνουμε τυχόν προηγούμενο υποψήφιο
		// πλακίδιο κεφαλής και θέτουμε νέο υποψήφιο πλακίδιο κεφαλής το
		// ανά χείρας πλακίδιο.

		if (kefali) kefali.kefaliCandiDOM(false);
		gipedo.kefali = this;
		if (gipedo.kefali !== skolix.kefali().plakidioGet()) {
			this.kefaliCandiDOM(true);
			return this;
		}

		skolix.kefali().createDOM('kefali');
		return this;
	}

	// Έχουμε κάνει κλικ δεύτερη φορά στο ίδιο πλακίδιο. Σ' αυτή την περίπτωση και
	// εφόσον δεν υπάρχει σκουλήκι, δημιουργούμε νέο σκουλήκι με κεφαλή στο
	// συγκεκριμένο πλακίδιο.

	if (!skolix) {
		gipedo.skolixCreate();
		gipedo.cpanelSTART.css('display', 'block');
		gipedo.cpanelSTOP.css('display', 'none');
		return this;
	}

	// Υπάρχει σκουλήκι και εφόσον το πλακίδιο που επιλέξαμε δεν είναι η κεφαλή
	// του σκουληκιού θα πρέπει να διαγραφεί το σκουλήκι και να δημιουργηθεί νέο
	// σκουλήκι.

	if (skolix.kefali().plakidioGet() != gipedo.kefali) {
		if (skolix.epomeniKinisi) {
			skolix.diakopi();
			gipedo.cpanelSTOP.css('display', 'none');
		}

		gipedo.kefali = this;
		gipedo.skolixCreate();
		return this;
	}

	// Σ' αυτό το σημείο απλώς καταργούμε οποιαδήποτε δρομολογημένη κίνηση και
	// δημιουργούμε νέο σκουλήκι με κεφαλή στο συγκεκριμένο πλακίδιο. Θέτουμε
	// το νέο σκουλήκι σε κιατάσταση ετοιμότητας.

	if (skolix.epomeniKinisi) {
		skolix.diakopi();
		gipedo.skolixCreate();
		gipedo.cpanelSTART.css('display', 'block');
		gipedo.cpanelSTOP.css('display', 'none');
		return this;
	}

	// Αν δεν υπάρχει δρομολογημένη κίνηση, απλώς καταργούμε οποιαδήποτε υποψήφιο
	// πλακίδιο κεφαλής και (επαν)εκκινούμε την κίνηση του σκουληκιού.

	gipedo.kefaliCandiClear().skolix.kinisiStart();
	return this;
}

// Η μέθοδος "kefaliCandiDOM" δέχεται ένα πλακίδιο και το καθιστά υποψήφιο πλακίδιο
// κεφαλής. Αν δεν περάσουμε πλακίδιο, τότε διαγράφουμε οποιαδήποτε υποψηφιότητα
// πλακιδίου κεφαλής.

Plakidio.prototype.kefaliCandiDOM = function(plakidio) {
	var gipedo = this.gipedo, size;

	if (!plakidio) {
		this.DOM.children('.plakidioKefalis').remove();
		gipedo.cpanelKCC.css('display', 'none');
		if (!gipedo.skolix) gipedo.cpanelSTART.css('display', 'none');
		return this;
	}

	size = parseInt(gipedo.size / 2) + 'px';
	this.DOM.append($('<img>').addClass('plakidioKefalis').css({
		width: size,
		height: size,
	}).attr({
		title: 'Click me!',
		src: 'ikona/skolix/kefali.png',
	}));
	gipedo.cpanelKCC.css('display', 'block');
	if (gipedo.skolix) gipedo.skolix.kefaliIcon('diakopi');
	return this;
}

// Η μέθοδος "katilimeno" επιστρέφει true εφόσον το πλακίδιο είναι κατειλημμένο από
// κύτταρο του σκουληκιού.

Plakidio.prototype.katilimeno = function() {
	return this.kitaro;
}

// Η μέθοδος "diathesimo" επιστρέφει true εφόσον το πλακίδιο είναι δεν είναι κατειλημμένο
// από κύτταρο του σκουληκιού.

Plakidio.prototype.diathesimo = function() {
	return !this.katilimeno();
}

// Η μέθοδος "ekenosi" καθιστά το πλακίδιο ελεύθερο αποδεσμεύοντάς το από οποιοδήποτε
// κύτταρο του σκουληκιού.

Plakidio.prototype.ekenosi = function() {
	delete this.kitaro;
	return this;
}

// Η μέθοδος "ixnosAdd" αυξάνει το πλήθος των διαβάσεων του σκουληκιού από το ανά χείρας
// πλακίδιο και ενημερώνει τη σχετική ένδειξη.

Plakidio.prototype.ixnosAdd = function() {
	this.ixnosDOM.text(++this.ixnos);
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Η κλάση "Skolix" παριστά το ένα σκουλήκι. Το σκουλήκι αποτελείται από διατεταγμένα
// κύτταρα εκ των οποίων το πρώτο ονομάζεται ουρά και το τελευταίο κεφαλή του σκουληκιού.
// Το σκουλήκι δημιουργείται με βάση το υποψήφιο πλακίδιο κεφαλής που έχει καθοριστεί με
// δύο διαδοχικά κλικ στο ίδιο πλακίδιο.

Skolix = function(gipedo) {
	var kefali = gipedo.kefali, row, col;

	kefali.kefaliCandiDOM(false);
	this.gipedo = gipedo;
	this.kitaro = [];

	// Το σκουλήκι εκτείνεται βουστροφηδόν ξεκινώντας από την ουρά στο πάνω αριστερά
	// πλακίδιο του τερέν και καταλήγοντας στο πλακίδιο κεφαλής.

	for (row = 1; row < kefali.row; row++) {
		if (row % 2) {
			for (col = 1; col <= gipedo.cols; col++) {
				this.kitaroAdd(new Kitaro(this, row, col));
			}
		}
		else {
			for (col = gipedo.cols; col >= 1; col--) {
				this.kitaroAdd(new Kitaro(this, row, col));
			}
		}
	}

	// Τελευταία δημιουργούμε τα κύτταρα που βρίσκονται στη γραμμή της κεφαλής.

	if (kefali.row % 2) {
		for (col = 1; col <= kefali.col; col++) {
			this.kitaroAdd(new Kitaro(this, kefali.row, col));
		}
	}
	else {
		for (col = gipedo.cols; col >= kefali.col; col--) {
			this.kitaroAdd(new Kitaro(this, kefali.row, col));
		}
	}
}

// Η "kitaroAdd" δέχεται ένα κύτταρο και το προσθέτει στο ανά χείρας σκουλήκι στη
// θέση της κεφαλής.

Skolix.prototype.kitaroAdd = function(kitaro) {
	kitaro.skolix = this;
	this.kitaro.push(kitaro);
	return this;
}

// Η μέθοδος "kitaroGet" επιστρέφει συγκκριμένο κύτταρο του σκουληκιού. Ως παράμετρο
// περνάμε την τάξη του ζητουμένου κυττάρου (0 για την ουρά).

Skolix.prototype.kitaroGet = function(i) {
	return this.kitaro[i];
}

// Η μέθοδος "mikos" επιστρέφει το μήκος του σκουληκιού, ήτοι το πλήθος των κυττάρων
// που το αποτελούν.

Skolix.prototype.mikos = function() {
	return this.kitaro.length;
}

// Η μέθοδος "kefali" επιστρέφει το τελευταίο κύτταρο του σκουληκιού, δηλαδή το κεφάλι.

Skolix.prototype.kefali = function() {
	return this.kitaroGet(this.mikos() - 1);
}

// Η μέθοδος "oura" επιστρέφει το πρώτο κύτταρο του σκουληκιού, δηλαδή την ουρά.

Skolix.prototype.oura = function() {
	return this.kitaroGet(0);
}

// Η μέθοδος "kitaroWalk" διατρέχει όλα τα κύτταρα του σκουληκιού από την ουρά προς
// την κεφαλή και για κάθε κύτταρο καλεί callback function ως μέθοδο του επισκεπτομένου
// κυττάρου.

Skolix.prototype.kitaroWalk = function(callback) {
	var i, mikos = this.mikos();

	for (i = 0; i < mikos; i++) {
		callback.apply(this.kitaroGet(i));
	}

	return this;
}

// Η μέθοδος "createDOM" δημιουργεί DOM elements για κάθε κύτταρο του σκουληκιού.
// Τα DOM elements των κυττάρων προσαρτώνται στο πλακάκι που καταλαμβάνει το
// κύτταρο με αποτέλεσμα να εμφανίζεται το σκουλήκι στη σελίδα.

Skolix.prototype.createDOM = function() {
	var i, mikos = this.mikos();

	if (mikos < 1) return this;

	for (i = mikos - 2; i > 0; i--) {
		this.kitaroGet(i).createDOM('soma');
	}

	this.oura().createDOM('oura');
	this.kefali().createDOM('kefali');

	return this;
}

// Η μέθοδος "removeDOM" καταργεί τα DOM elements των κυττάρων και έχει ως παρενέργεια
// τη διαγραφή του σκουληκιού από το DOM.

Skolix.prototype.removeDOM = function() {
	var i, kitaro;

	for (i = this.mikos() - 1; i >= 0; i--) {
		kitaro = this.kitaroGet(i);
		if (kitaro.hasOwnProperty('DOM')) kitaro.DOM.remove();
	}

	return this;
}

// Η μέθοδος "kefaliIcon" δέχεται το όνομα ενός αρχείου εικόνας και θέτει ανάλογα
// την εικόνα της κεφαλής του σκουληκιού.

Skolix.prototype.kefaliIcon = function(ikona) {
	this.kefali().DOM.attr('src', 'ikona/skolix/' + ikona + '.png');
	return this;
}

// Η μέθοδος "kinisiStart" (επαν)εκκινεί την κίνηση του σκουληκιού. Η κίνηση επανεκκινεί
// ασύγχρονα με ελάχιστη χρονοκαθυστέρηση.

Skolix.prototype.kinisiStart = function() {
	var skolix = this;

	// Αν υπάρχει ήδη δρομολογημένη κίνηση, τότε αυτή αναιρείται και αμέσως μετά
	// δρομολογείται νέα κίνηση με ελάχιστη χρονοκαθυστέρηση.

	if (this.epomeniKinisi) clearTimeout(this.epomeniKinisi);
	this.epomeniKinisi = setTimeout(function() {
		skolix.kinisi();
	}, SkOOlix.speedMin);

	// Θέτουμε πλήκτρα εκκίνησης/διαοκπής στο control panel και αλλάζουμε την
	// εικόνα της κεφαλής σε φατσούλα κίνησης.

	this.gipedo.cpanelSTART.css('display', 'none');
	this.gipedo.cpanelSTOP.css('display', 'block');
	this.kefali().createDOM('kinisi');
	return this;
}

// Η μέθοδος "kinisiStop" διακόπτει την κίνηση του σκουληκιού ακυρώνοντας τυχόν
// δρομολογημένη κίνηση. Παράλληλα θέτει τα πλήκτρα εκκίνησης/διακοπής στο
// control panel και αλλαζει την εικόνα της κεφαλής σε φατσούλα αναμονής.


Skolix.prototype.kinisiStop = function() {
	if (this.epomeniKinisi) clearTimeout(this.epomeniKinisi);
	this.gipedo.cpanelSTART.css('display', 'block');
	this.gipedo.cpanelSTOP.css('display', 'none');
	this.kefali().createDOM('anamoni');
	return this;
}

// Η μέθοδος "kinisi" είναι αυτή που κινεί το σκουλήκι. Ουσιαστικά μετακινείται η κεφαλή
// σε κάποιο διπλανό (όχι διαγώνιο) πλακίδιο και όλα τα κύτταρα του σκουληκιού μετακινούνται
// στη θέση που βρίσκονταν πριν το προηγούμενο κύτταρο.

Skolix.prototype.kinisi = function() {
	var skolix = this, gipedo = this.gipedo, kefali, apopira, row, col, plakidio, strofi;

	// Εντοπίζουμε το πλακίδιο της κεφαλής του σκουληκιού και αποφασίζουμε την κατεύθυνση
	// της επικείμενης κίνησης.

	kefali = this.kefali().plakidioGet();
	this.apofasi();

	// Εκκινούμε κύκλο ελέγχων του πλακιδίου προορισμού της κεφαλής με βάση την κατεύθυνση
	// που επιλέχθηκε πριν.

	for (apopira = 1; true; apopira++) {
		row = kefali.row;
		col = kefali.col;

		// Οι δυνατές κατευθύνσεις είναι: 0 (πάνω), 1 (δεξιά), 2 (κάτω) και 3 (αριστερά).
		// Υπάρχουν και οι αντίστοιχες αρνητικές τιμές.

		switch (this.poria % 4) {
		case 0:
			row--;
			break;
		case 1:
		case -3:
			col++;
			break;
		case 2:
		case -2:
			row++;
			break;
		default:
			col--;
			break;
		}

		// Γίνεται έλεγχος διαθεσιμότητας του πλακιδίου προορισμού της κεφαλής. Το πλακίδιο
		// πρέπει να βρίσκεται εντός τερέν και να μην είναι ήδη κατειλημμένο από κάποιο
		// κύτταρο του σκουληκιού. Η στροφή της κεφαλή προς τη νέα κατεύθυνση γίνεται με
		// σήκωμα της κεφαλής στον άξονα των z, επομένως δεν περιοριζόμαστε από τα όρια
		// του τερέν όσον αφορά στην ελευθερία της κίνησης.

		if ((row >= 1) && (row <= gipedo.rows) && (col >= 1) && (col <= gipedo.cols) &&
			(plakidio = gipedo.plakidioGet(row, col)).diathesimo()) break;

		// Αν το πλακίδιο προορισμού δεν είναι δεκτό, τότε επιλέγουμε νέο διατρέχοντας
		// κυκλικά όλες τις δυνατές θέσεις. Η επιλογή του νέου πλακιδίου γίνεται προς
		// τα δεξιά ή προς τα αριστερά με πιθανότητα 50/50.

		if (apopira == 1) strofi = (Math.random() < 0.5 ? -1 : 1);

		// Αν έχουμε κάνει τέσσερις ανεπιτυχείς απόπειρες επιλογής πλακιδίου προορισμού
		// κεφαλής, τότε σημαίνει ότι δεν υπάρχει διαθέσιμο πλακίδιο μετακίνησης κεφαλής
		// επομένως έχουμε ακινητοποίηση του σκουληκιού.

		else if (apopira > 3) return this.adiexodo();

		// Αλλιώς δοκιμάζουμε νέα πορεία με τη φορά που έχουμε ήδη επιλέξει.

		this.poria += strofi;
	}

	// Έχουμε περάσει τους κύκλους ελέγχου, επομένως έχει επιλεγεί πλακίδιο προορισμού της
	// κεφαλής του σκουληκιού και προχωρούμε στη μετακίνσηση.

	this.metakinisi(plakidio);

	// Μετά το πέρας της μετακίνησης δρομολογείται η επόμενη κίνηση, εκτός και αν έχει τεθεί
	// η ελάχιστη δυνατή ταχύτητα, οπότε η κίνηση θα γίνεται με κλικ στο χελωνάκι.

	if (gipedo.speed < SkOOlix.speedMax) this.epomeniKinisi = setTimeout(function() {
		skolix.kinisi();
	}, gipedo.speed);
	return this;
}

// Η μέθοδος "apofasi" είναι αυτή που καθορίζει τη νέα πορεία του σκουληκιού. Το σκουλήκι
// δεν αλλάζει κατεύθυνση σε κάθε κίνηση, αλλά μπορεί να διατηρεί την πορεία που είχε ήδη
// ανάλογα με το βαθμό ελευθερίας που έχουμε καθορίσει.

Skolix.prototype.apofasi = function() {
	if (!this.hasOwnProperty('poria')) return this.neaPoria();
	if (parseInt(Math.random() * this.gipedo.epimoni)) return this;
	return this.neaPoria();
}

// Εφόσον κρίθηκε ότι έπρεπε να αποφασιστεί νέα πορεία, καλείτα η μέθοδος "neaPoria" η οποία
// μια από τις τέσσερις πορείες που μπορεί να ακολουθεί το σκουλήκι: 0 (πάνω), 1 (δεξιά),
// 2 (κάτω) και 3 (αριστερά).

Skolix.prototype.neaPoria = function() {
	this.poria = parseInt(Math.random() * 4);
	return this;
}

// Η μέθοδος "metakinisi" μετακινεί το κεφάλι του σκουληκιού στο πλακίδιο το οποίο
// περνάμε ως παράμετρο. Φυσικά, μετακινούνται ανάλογα όλα τα κύτταρα του σκουληκιού.

Skolix.prototype.metakinisi = function(plakidioKefalis) {
	var i, kefali = this.mikos() - 1, gipedo = this.gipedo;

	this.oura().plakidioGet().ekenosi();
	for (i = 0; i < kefali; i++) {
		this.kitaroGet(i).plakidioSet(this.kitaroGet(i + 1).plakidioGet());
	}
	this.kitaroGet(kefali).plakidioSet(plakidioKefalis);

	// Ως γνωστόν, όταν κάνουμε προσάρτηση κάποιου DOM element σε κάποιο άλλο
	// μέσω jQuery (append, prepend κλπ), ελέγχεται αν το προσαρτώμενο DOM
	// element είναι ήδη προσαρτημένο σε άλλο σημείο του DOM και αν ναι, τότε
	// απεξαρτάται από την προηγούμενη θέση του.

	this.kitaroWalk(function() {
		this.plakidioGet().DOM.append(this.DOM);
	});
	
	this.kefali().plakidioGet().ixnosAdd();
	return this;
}

// Η μέθοδος "adiexodo" καλείται όταν το σκουλήκι εγκλωβιστεί και η καφαλή του δεν μπορεί
// να μετακινηθεί σε κανένα διπλανό του τεράγωνο.

Skolix.prototype.adiexodo = function() {
	this.kefaliIcon('adiexodo');
	this.gipedo.cpanelSTOP.css('display', 'none');
	return this;
}

// Η μέθοδος "diakopi" καλείται κάθε φορά που διακόπτουμε την κίνηση του σκουληκιού με σκοπό
// να δημιουργήσουμε νέο σκουλήκι.

Skolix.prototype.diakopi = function() {
	clearTimeout(this.epomeniKinisi);
	delete this.epomeniKinisi;
	this.gipedo.plakidioWalk(function() {
		this.ixnos = 0;
		this.ixnosDOM.empty();
	});
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Η κλάση "Kitaro" παριστά τα κύτταρα του σκουληκιού. Για τη δημιουργία νέου κυττάρου
// απαιτείται το σκουλήκι στο οποίο θα προσαρτηθεί το κύτταρο και τα στοιχεία θέσης τού
// πλακιδίου στο οποίο θα εμφανιστεί το νεόκοπο κύτταρο.

Kitaro = function(skolix, row, col) {
	this.plakidioSet(skolix.gipedo.plakidioGet(row, col));
}

// Η μέθοδος "plakidioSet" δέχεται ως παράμετρο ένα πλακίδιο και το συσχετίζει με το ανά
// χείρας κύτταρο τοποθετώντας το συγκεκριμένο κύτταρο στο συγκεκριμένο πλακίδιο.

Kitaro.prototype.plakidioSet = function(plakidio) {
	this.plakidio = plakidio;
	plakidio.kitaro = this;
	return this;
}

// Η μέθοδος "plakidioGet" επιστρέφει το πλακίδιο στο οποίο βρίσκεται το ανά χείρας κύτταρο.

Kitaro.prototype.plakidioGet = function() {
	return this.plakidio;
}

// Η μέθοδος "createDOM" δημιουργεί το DOM element του ανά χείρας κυττάρου και το προσαρτά
// στο αντίστοιχο πλακίδιο.

Kitaro.prototype.createDOM = function(img) {
	if (this.hasOwnProperty('DOM')) this.DOM.remove();
	this.DOM = $('<img>').addClass('kitaro').css('width', SkOOlix.size + 'px').
	attr('src', 'ikona/skolix/' + img + '.png').
	appendTo($(this.plakidio.DOM));
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Η function "gipedoWalk" διατρέχει τα γήπεδα και για κάθε γήπεδο καλεί callback
// function ως μέθοδο του γηπέδου.

SkOOlix.gipedoWalk = function(callback) {
	var i;

	for (i = 0; i < SkOOlix.multi; i++) {
		callback.apply(SkOOlix.gipedo[i]);
	}

	return SkOOlix;
}
